package ar.uba.fi.tonyvaliente.documents;

import java.util.NoSuchElementException;

/**
 * Representa un filtro que aplica el operador OR sobre los
 * elementos recuperados por otros dos filtros cualesquiera.
 * Esta implementado de modo tal que no necesite generar
 * una nueva coleccion con los elementos de ambos iteradores
 * @author dmorello
 */
public class DocumentORFilter extends DocumentBinaryOperatorFilter {

	public DocumentORFilter(DocumentFilter filter1, DocumentFilter filter2,
			boolean negated) {
		super(filter1, filter2, negated);
	}

	/**
	 * Crea un ResultIterator capaz de aplicar el operador OR
	 * sobre los resultados devueltos por los ResultIterators
	 * correspondientes a los filtros asociados a esta instancia
	 */
	@Override
	protected ResultIterator createResultIterator(int totalDocumentos) {
		ResultIterator it1 = filter1.createResultIterator(totalDocumentos);
		ResultIterator it2 = filter2.createResultIterator(totalDocumentos);
		return new DocumentORResultIterator(it1, it2, totalDocumentos);
	}
	
	/**
	 * ResultIterator capaz de aplicar el operador OR
	 * sobre los resultados devueltos por los ResultIterators
	 * correspondientes a los filtros asociados a esta instancia
	 */
	private class DocumentORResultIterator extends ResultIterator {
		
		private ResultIterator it1;
		private ResultIterator it2;
		private int id1;
		private int id2;
		private int nextId;
		
		/**
		 * Crea una instancia de la clase que aplica el operador AND
		 * sobre los elementos devueltos por otros dos iteradores
		 * @param it1 un ResultIterator cualquiera
		 * @param it2 un ResultIterator cualquiera
		 * @param totalDocumentos total de documentos del indice invertido
		 */
		public DocumentORResultIterator(ResultIterator it1,
					ResultIterator it2, int totalDocumentos) {
			super(totalDocumentos);
			this.it1 = it1;
			this.it2 = it2;
			
			// Seteo estos valores para poder avanzar cada uno
			// de los iteradores cuando sea necesario
			id1 = 0;
			id2 = 0;
			nextId = 0;
		}
		
		public boolean hasNext() {
			return it1.hasNext() || it2.hasNext();
		}
		
		public Integer next() {
			
			// Verifico si el ultimo elemento dado por it1
			// es el que se devolvio en la llamada anterior a next()
			if (id1 <= totalDocumentos && nextId == id1) {
				// Debo pasar al proximo elemento del iterador (si lo hay)
				if (it1.hasNext()) {
					id1 = it1.next();
				} else {
					// Si no hay, se setea este valor para que no se avance mas
					id1 = totalDocumentos + 1;
				}
			}
			
			// Hago lo mismo con it2
			if (id2 <= totalDocumentos && nextId == id2) {
				if (it2.hasNext()) {
					id2 = it2.next();
				} else {
					id2 = totalDocumentos + 1;
				}
			}
			
			// Verifico si llegue al fin de ambos iteradores
			if (id1 > totalDocumentos && id2 > totalDocumentos) {
				throw new NoSuchElementException("No hay mas elementos en el iterador");
			}
			
			// El menor de los dos elementos tomados sera el siguiente
			nextId = Math.min(id1, id2);
			
			return nextId;
		}
	}

}
